#include <wiringPi.h>
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
typedef struct Data
{
    double temp, humid;
} Data;

Data _readfromdht(int DHT_GPIO, bool USING_DHT11, int LH_THRESHOLD)
{
    // printf("%d %d %d\n", DHT_GPIO, USING_DHT11, LH_THRESHOLD);
    // printf("Start\n");
    // fflush(stdin);
    int humid = 0, temp = 0;
    piBoardRev();
    wiringPiSetupGpio();
    piHiPri(99);
    unsigned char data[5] = {0, 0, 0, 0, 0};
    TRYAGAIN: // If checksum fails (come back here)
    memset(data, 0, sizeof(data));
    pinMode(DHT_GPIO, OUTPUT);    // gpio starts as output
    digitalWrite(DHT_GPIO, LOW);  // pull the line low
    usleep(18000);                // wait for 18ms
    digitalWrite(DHT_GPIO, HIGH); // set the line high
    pinMode(DHT_GPIO, INPUT);     // now gpio is an input
    // need to ignore the first and second high after going low
    do
    {
        delayMicroseconds(1);
        // puts(".");
    } while (digitalRead(DHT_GPIO) == HIGH);
    do
    {
        delayMicroseconds(1);
    } while (digitalRead(DHT_GPIO) == LOW);
    do
    {
        delayMicroseconds(1);
    } while (digitalRead(DHT_GPIO) == HIGH);
    // Remember the highs, ignore the lows -- a good philosophy!
    for (int d = 0; d < 5; d++)
    { // for each data byte
        // read 8 bits
        for (int i = 0; i < 8; i++)
        { // for each bit of data
            do
            {
                delayMicroseconds(1);
            } while (digitalRead(DHT_GPIO) == LOW);
            int width = 0; // measure width of each high
            do
            {
                width++;
                delayMicroseconds(1);
                if (width > 1000)
                    break;                           // missed a pulse -- data invalid!
            } while (digitalRead(DHT_GPIO) == HIGH); // time it!
            // shift in the data, msb first if width > the threshold
            data[d] = data[d] | ((width > LH_THRESHOLD) << (7 - i));
        }
    }
    if (USING_DHT11)
    {
        humid = data[0] * 10; // one byte - no fractional part
        temp = data[2] * 10;  // multiplying to keep code concise
    }
    else
    {                                     // for DHT22 (AM2302/AM2301)
        humid = (data[0] << 8 | data[1]); // shift MSBs 8 bits left and OR LSBs
        temp = (data[2] << 8 | data[3]);  // same again for temperature
    }
    unsigned char chk = 0; // the checksum will overflow automatically
    for (int i = 0; i < 4; i++)
    {
        chk += data[i];
    }
    if (chk != data[4])
    {
        // printf("Checkout fail\n");
        usleep(2000000); // have to delay for 1-2 seconds between readings
        goto TRYAGAIN;   // a GOTO!!! call yourself a C/C++ programmer!
    }
    piBoardRev();
    struct Data ret;
    ret.temp = (double)temp / 10;
    ret.humid = (double) humid / 10;
    return ret;
}

static PyObject *readfromdht(PyObject *self, PyObject *args)
{
    int DHT_GPIO, USING_DHT11 ,LH_THRESHOLD ;
    if (!PyArg_ParseTuple(args, "iii", &DHT_GPIO, &USING_DHT11, &LH_THRESHOLD))
        return NULL;
    Data ret = _readfromdht(DHT_GPIO, USING_DHT11, LH_THRESHOLD);
    return Py_BuildValue("dd", ret.temp, ret.humid);
}

static PyMethodDef dht_methods[] = {
    {"readfromdht", readfromdht, METH_VARARGS},
    {NULL, NULL, 0, NULL}};

static struct PyModuleDef dht_module = {
    PyModuleDef_HEAD_INIT,
    "dht",
    NULL,
    -1,
    dht_methods};

PyMODINIT_FUNC PyInit_hw6_dht(void)
{
    return PyModule_Create(&dht_module);
}

